package com.ea.admin.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ea.admin.mapper.DataSourceMapper;
import com.ea.admin.pojo.param.datasource.*;
import com.ea.admin.pojo.po.DataSource;
import com.ea.admin.pojo.vo.datasource.*;
import com.ea.admin.service.DataSourceService;
import com.ea.common.utils.IpUtils;
import com.ea.common.result.BizException;
import com.ea.common.result.ErrorCode;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class DataSourceServiceImpl implements DataSourceService {
    
    @Resource
    private DataSourceMapper dataSourceMapper;
    
    @Resource
    private HttpServletRequest request;
    
    @Override
    public GetDataSourceListVo getDataSourceList(Long accountId, String name, String sourceIp, Integer page, Integer pageSize) {
        Page<DataSource> dataSourcePage = dataSourceMapper.selectDataSourceList(
            new Page<>(page, pageSize), name, sourceIp, 1);

        List<DataSourceVo> dataSourceVos = dataSourcePage.getRecords().stream()
            .map(this::convertToVo)
            .collect(Collectors.toList());
        
        GetDataSourceListVo result = new GetDataSourceListVo();
        result.setTotal(dataSourcePage.getTotal());
        result.setCurrentPage(page);
        result.setPageSize(pageSize);
        result.setDataSourceList(dataSourceVos);
        
        return result;
    }
    
    @Override
    public DataSourceVo addDataSource(Long accountId, AddDataSourceParam param) {
        if (dataSourceMapper.checkSourceNameExists(param.getSourceName(), null)) {
            throw new BizException(ErrorCode.SYSTEM_RESOURCE_ERROR_NAME_EXISTS);
        }

        String sourceIp = IpUtils.getIpAddr(request);

        DataSource dataSource = new DataSource();
        dataSource.setSourceName(param.getSourceName());
        dataSource.setSourceIp(sourceIp);
        dataSource.setIsActive(1);
        dataSource.setCreateTime(LocalDateTime.now());
        dataSource.setUpdateTime(LocalDateTime.now());
        
        // 生成令牌
        String token = generateTokenByNameAndIp(param.getSourceName(), sourceIp);
        dataSource.setSourceToken(token);
        
        dataSourceMapper.insert(dataSource);
        
        return convertToVo(dataSource);
    }
    
    @Override
    public DataSourceVo editDataSource(Long accountId, EditDataSourceParam param) {
        // 检查数据源是否存在
        DataSource dataSource = dataSourceMapper.selectById(param.getSourceId());
        if (dataSource == null) {
            throw new BizException(ErrorCode.SYSTEM_RESOURCE_ERROR_DATA_SOURCE_NOT_EXISTS);
        }

        if (!dataSource.getSourceName().equals(param.getSourceName()) && 
            dataSourceMapper.checkSourceNameExists(param.getSourceName(), Long.valueOf(param.getSourceId()))) {
            throw new BizException(ErrorCode.SYSTEM_RESOURCE_ERROR_NAME_EXISTS);
        }
        
        // 如果名称发生变化，重新生成令牌
        if (!dataSource.getSourceName().equals(param.getSourceName())) {
            String token = generateTokenByNameAndIp(param.getSourceName(), dataSource.getSourceIp());
            dataSource.setSourceToken(token);
        }

        dataSource.setSourceName(param.getSourceName());
        dataSource.setIsActive(param.getIsActive());
        dataSource.setUpdateTime(LocalDateTime.now());
        
        dataSourceMapper.updateById(dataSource);

        return convertToVo(dataSource);
    }
    
    @Override
    public String deleteDataSource(Long accountId, DeleteDataSourceParam param) {
        DataSource dataSource = dataSourceMapper.selectById(param.getSourceId());
        if (dataSource == null) {
            throw new BizException(ErrorCode.SYSTEM_RESOURCE_ERROR_DATA_SOURCE_NOT_EXISTS);
        }

        dataSourceMapper.deleteById(param.getSourceId());
        return dataSource.getSourceName();
    }

    
    @Override
    public Long getDataSourceByToken(String token) {
        if (!StringUtils.hasText(token)) {
            throw new BizException(ErrorCode.USER_PARAM_ERROR_TOKEN_INVALID);
        }
        
        // 根据令牌查询数据源
        DataSource dataSource = dataSourceMapper.selectBySourceToken(token);
        if (dataSource == null) {
            throw new BizException(ErrorCode.USER_PARAM_ERROR_TOKEN_INVALID);
        }
        
        // 检查数据源是否激活
        if (dataSource.getIsActive() != 1) {
            throw new BizException(ErrorCode.SYSTEM_RESOURCE_ERROR_DATA_SOURCE_INACTIVE);
        }
        
        return dataSource.getSourceId();
    }

    @Override
    public String getDataSourceNameById(Long dataSourceId) {
        LambdaQueryWrapper<DataSource> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DataSource::getSourceId, dataSourceId)
                    .eq(DataSource::getIsActive, 1)
                    .eq(DataSource::getIsDel, 0);
        DataSource dataSource = dataSourceMapper.selectOne(queryWrapper);
        if (dataSource != null) {
            return dataSource.getSourceName();
        }

        return null;
    }

    /**
     * 基于名称和IP生成令牌
     */
    private String generateTokenByNameAndIp(String name, String ip) {
        String input = name + ip + System.currentTimeMillis();
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
            StringBuilder hexString = new StringBuilder();
            for (byte b : hash) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("SHA-256 algorithm not available", e);
        }
    }
    
    /**
     * DataSource 2 DataSourceVo
     */
    private DataSourceVo convertToVo(DataSource dataSource) {
        DataSourceVo vo = new DataSourceVo();
        vo.setSourceName(dataSource.getSourceName());
        vo.setSourceId(dataSource.getSourceId());
        vo.setSourceIp(dataSource.getSourceIp());
        vo.setSourceToken(dataSource.getSourceToken());
        return vo;
    }
}